﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using VisionCloud.Domain.DtoModel.Fms;
using VisionCloud.Utility;
using Point = VisionCloud.Domain.DtoModel.Fms.Point;

namespace VisionCloud.Service.Algorithm
{
    public class AlgorithmService
    {
        /// <summary>
        /// 天花分格大小
        /// </summary>
        private readonly int CeilgridSize = 100;

        /// <summary>
        /// 天花校准
        /// </summary>
        /// <param name="tempJson">模板Json对象</param>
        /// <param name="resultJson">结果Json对象</param>
        /// <param name="setParam"></param>
        /// <returns></returns>
        public bool SetBoundaryforCeilingResult(SaveCeilingFloorRqDto tempJson, Ceiling3DJsonResult resultJson, SetBoundaryforCeilingParam setParam)
        {
            bool inside = false;
            // 1. 记录映射关系
            List<string> Pos2FMS = new List<string>();

            // 2. 遍历模板边界
            tempJson.RoomList.ForEach(room =>
            {
                List<int> bd_rooms = new List<int>();
                List<FusePf> bd_line = new List<FusePf>();
                // 3. 遍历结果站点，改造数据
                int j = -1;
                resultJson.Data.ForEach(ceilData =>
                {
                    j++;
                    // 4. 遍历龙骨
                    ceilData?.Data.ForEach(pflg =>
                    {
                        if (setParam.NeedDeelCeiling)
                        {
                            pflg.Type = 7;
                        }
                        float x1 = (float)pflg.Location[0].X;
                        float y1 = (float)pflg.Location[0].Y;
                        float x2 = (float)pflg.Location[1].X;
                        float y2 = (float)pflg.Location[1].Y;

                        // 5. 转化FMS边界至边阔
                        // 6. 一旦端点都在轮廓内 即配准
                        if (IsInPolygon(room.Boundary, new Point { X = (x1 + x2) * 0.5, Y = (y1 + y2) * 0.5 }))
                        {
                            inside = true;
                            if (!(bd_rooms.Exists(t => t == j)))//判断不存在
                            {
                                bd_rooms.Add(j);
                            }
                            if (room.CeilHeight < 100)
                            {
                                room.CeilHeight = ceilData.CeilHeight;
                            }
                            // 7. 转局部坐标
                            double angle = -room.Angle;
                            Point pnt_zero = new Point { X = room.OriginPoint.X, Y = room.OriginPoint.Y };
                            double pi = 3.1416;
                            int xx1 = (int)(((x1 - pnt_zero.X) * Math.Cos(angle * pi / 180)) - (y1 - pnt_zero.Y) * Math.Sin(angle * pi / 180));
                            int yy1 = (int)(((y1 - pnt_zero.Y) * Math.Cos(angle * pi / 180)) + (x1 - pnt_zero.X) * Math.Sin(angle * pi / 180));
                            int xx2 = (int)(((x2 - pnt_zero.X) * Math.Cos(angle * pi / 180)) - (y2 - pnt_zero.Y) * Math.Sin(angle * pi / 180));
                            int yy2 = (int)(((y2 - pnt_zero.Y) * Math.Cos(angle * pi / 180)) + (x2 - pnt_zero.X) * Math.Sin(angle * pi / 180));
                            if (Math.Abs(xx1 - xx2) < CeilgridSize)
                            {
                                xx2 = xx1;
                            }
                            else if (Math.Abs(yy1 - yy2) < CeilgridSize)
                            {
                                yy2 = yy1;
                            }

                            FusePf tmp = new FusePf
                            {
                                Depth = (int)pflg.Depth,
                                Direction = (int)pflg.Direction,
                                Type = pflg.Type,
                                X1 = xx1,
                                Y1 = yy1,
                                X2 = xx2,
                                Y2 = yy2
                            };
                            bd_line.Add(tmp);

                            // 11. 遍历多传料口，必须有拼缝和龙骨
                            ceilData?.DataHole?.ForEach(hole =>
                            {
                                int g_min_x = 99999, g_min_y = 99999, g_max_x = -99999, g_max_y = -99999;
                                hole?.ForEach(p =>
                                {
                                    double g_x = p.X;
                                    double g_y = p.Y;
                                    float g_xx = (float)(((g_x - pnt_zero.X) * Math.Cos(angle * 3.1416 / 180) - (g_y - pnt_zero.Y)
                                    * Math.Sin(angle * pi / 180)) / CeilgridSize);
                                    float g_yy = (float)(((g_y - pnt_zero.Y) * Math.Cos(angle * 3.1416 / 180) + (g_x - pnt_zero.X) *
                                    Math.Sin(angle * 3.1416 / 180)) / CeilgridSize);
                                    if (g_xx > g_max_x) g_max_x = (int)Math.Ceiling(g_xx);
                                    if (g_yy > g_max_y) g_max_y = (int)Math.Ceiling(g_yy);
                                    if (g_xx < g_min_x) g_min_x = (int)Math.Floor(g_xx);
                                    if (g_yy < g_min_y) g_min_y = (int)Math.Floor(g_yy);
                                });
                                for (int col = g_min_x; col < g_max_x; col++)
                                {
                                    for (int row = g_min_y; row < g_max_y; row++)
                                    {
                                        if (room.Param == null) room.Param = new ParamDto() { UnPolishGrids = new List<Point>() };
                                        if (room.Param.UnPolishGrids == null) room.Param.UnPolishGrids = new List<Point>();
                                        room.Param.UnPolishGrids.Add(new Point { X = col, Y = row });
                                    }
                                }
                            });

                            ParamDto param = new ParamDto
                            {
                                Datas = room.Param?.Datas,
                                UnPolishGrids = room.Param?.UnPolishGrids,
                                GridSize = CeilgridSize
                            };
                            room.Param = param;
                            // 12. 地图添加链接
                            if (!Pos2FMS.Exists(x => x == ceilData.BIMMapCode))
                            {
                                Pos2FMS.Add(ceilData.BIMMapCode);

                                // 13. 调度json
                                VisionImage visionImage = new VisionImage();
                                string tempJsonFile = setParam.CeilingBaseDir + "CeilResult/relation.json";
                                CeilingRelationListDto jsonDoc_Pos = JsonHelper.ReadObjectFromJsonFile<CeilingRelationListDto>(tempJsonFile);
                                var relationTemp = jsonDoc_Pos?.Data?.Find(x => x.Name.Contains(ceilData.BIMMapCode));
                                if (relationTemp != null)
                                {
                                    visionImage.Width = relationTemp.Width;
                                    visionImage.Height = relationTemp.Length;

                                    string ceilPngPath = setParam.CeilingBaseDir + "CeilResult";
                                    FileInfo[] fileInfos = new DirectoryInfo(ceilPngPath).GetFiles("*.png");
                                    foreach (var file in fileInfos)
                                    {
                                        if (file.Name.Contains(ceilData.BIMMapCode))
                                        {
                                            visionImage.Url = setParam.StaticFileBaseUrl + "CeilResult/" + file.Name;
                                            visionImage.StartPoint = new Point3D
                                            {
                                                X = ceilData.Boundary[0].X,
                                                Y = ceilData.Boundary[0].Y,
                                                Z = 0
                                            };
                                            visionImage.EndPoint = new Point3D
                                            {
                                                X = ceilData.Boundary[2].X,
                                                Y = ceilData.Boundary[2].Y,
                                                Z = ceilData.CeilHeight
                                            };
                                            if (room != null && room.VisionImages == null)
                                            {
                                                room.VisionImages = new List<VisionImage>();
                                            }
                                            room.VisionImages.Add(visionImage);
                                            return;
                                        }
                                    }
                                }
                            }
                        }
                    });
                });
                List<DateValue> tmpDatas = new List<DateValue>();
                //该边界所有满足的拼缝进行算法融合
                if (bd_rooms?.Count() > 0 && bd_line?.Count() > 0)
                {
                    bd_line = Line_fuse(bd_line, 100, 100, 30);
                }
                //融合后的拼缝写入数组
                for (int bl_i = 0; bl_i < bd_line?.Count(); bl_i++)
                {
                    DateValue tmp_data_value = new DateValue
                    {
                        Location = new List<Point>(),
                        Direction = bd_line[bl_i].Direction,
                        Depth = bd_line[bl_i].Depth,
                        Type = bd_line[bl_i].Type
                    };
                    Point Point1 = new Point() { X = (double)bd_line[bl_i].X1, Y = (double)bd_line[bl_i].Y1 };
                    Point Point2 = new Point() { X = (double)bd_line[bl_i].X2, Y = (double)bd_line[bl_i].Y2 };
                    tmp_data_value.Location.Add(Point1);
                    tmp_data_value.Location.Add(Point2);
                    tmpDatas.Add(tmp_data_value);
                }
                if (room.Param == null)
                {
                    room.Param = new ParamDto();
                }
                room.Param.Datas = new List<DateValue>();
                if (tmpDatas.Count > 0)
                {
                    room.Param.Datas.AddRange(tmpDatas);
                }
            });
            // 14. 删除没有param的站点
            var roomSettings = new List<RoomSetting>();
            if (tempJson.RoomList != null)
                roomSettings.AddRange(tempJson.RoomList.Where(
                    x => x.Param != null
                    && x.Param.Datas?.Count > 0
                    && x.Param.GridSize > 0));
            tempJson.RoomList = roomSettings;
            tempJson.IsCollectionData = true;
            tempJson.RoomSettings = new List<RoomSetting>();
            tempJson.RoomSettings.AddRange(tempJson.RoomList);
            tempJson.RoomList.Clear();//清空没必要的数据
            tempJson.GrayMap?.Black?.Clear();
            tempJson.GrayMap?.Range?.Clear();
            tempJson.GrayMap?.Gray?.Clear();
            if (!inside)
            {
                Logger.Error($"UploadAlgorithmResultToFms：没有拼缝、龙骨或传料口在边界里");
            }

            return true;
        }

        /// <summary>
        /// 判断点是否在轮廓内
        /// </summary>
        /// <param name="polygonPoints"></param>
        /// <param name="checkPoint"></param>
        /// <returns></returns>
        public bool IsInPolygon(List<Point> polygonPoints, Point checkPoint)
        {
            bool inside = false;
            int pointCount = polygonPoints.Count;
            Point p1, p2;
            for (int i = 0, j = pointCount - 1; i < pointCount; j = i, i++)
            {
                p1 = polygonPoints[i];
                p2 = polygonPoints[j];
                if (checkPoint.Y < p2.Y)
                {
                    if (p1.Y <= checkPoint.Y)
                    {
                        if ((checkPoint.Y - p1.Y) * (p2.X - p1.X) > (checkPoint.X - p1.X) * (p2.Y - p1.Y))
                        {
                            inside = !inside;
                        }
                    }
                }
                else if (checkPoint.Y < p1.Y)
                {
                    if ((checkPoint.Y - p1.Y) * (p2.X - p1.X) < (checkPoint.X - p1.X) * (p2.Y - p1.Y))
                    {
                        inside = !inside;
                    }
                }
            }
            return inside;
        }

        /// <summary>
        /// 判断是否需要修改模板
        /// </summary>
        /// <param name="tempJson"></param>
        /// <returns></returns>
        public bool IsJsonModify(SaveCeilingFloorRqDto tempJson)
        {
            //先清空已有param
            List<RoomSetting> arr_roomlist_ori0 = tempJson.RoomList;
            List<RoomSetting> roomSettingList = new List<RoomSetting>();
            bool boundary_mustbeChanged = false;
            arr_roomlist_ori0?.ForEach(room =>
            {
                if (room.Param == null)
                {
                    room.Param = new ParamDto()
                    {
                        Datas = new List<DateValue>(),
                        UnPolishGrids = new List<Point>()
                    };
                }
                if (room.VisionImages == null)
                {
                    room.VisionImages = new List<VisionImage>();
                }
                //将param和visionImages清空
                RoomSetting newRoom = room;
                if (newRoom.Param?.Datas != null)
                {
                    newRoom.Param.Datas.Clear();
                }
                if (newRoom.Param?.UnPolishGrids != null)
                {
                    newRoom.Param.UnPolishGrids.Clear();
                }
                if (newRoom?.VisionImages != null)
                {
                    newRoom.VisionImages.Clear();
                }
                //boundary_mustbeChanged = false的情况下，将param数组中的datas和unPolishGrids，还有visionImages数组清空
                ParamDto param = room.Param;
                roomSettingList.Add(newRoom);

                //处理非左下角零点 调FMS接口置零
                double angle = room.Angle;
                if (Math.Abs(angle - 90) < 1 || (angle < 90 && angle > 45))
                {
                    RoomSetting tmpRoom = room;
                    if (Math.Abs(tmpRoom.Angle - 90) < 1)
                    {
                        tmpRoom.Angle = 0;
                    }
                    else
                    {
                        tmpRoom.Angle = room.Angle - 90;
                    }
                    boundary_mustbeChanged = true;
                    float LBpnt_x = (float)(5.0 / 0.0), LBpnt_y = (float)(5.0 / 0.0);//表示无穷大
                    List<Point> boundary_tmp = newRoom.Boundary;
                    for (int bo_i = 0; bo_i < boundary_tmp.Count; bo_i++)//找左下角
                    {
                        double X_tmp = boundary_tmp[bo_i].X;
                        double Y_tmp = boundary_tmp[bo_i].Y;
                        if (LBpnt_x > (float)X_tmp) LBpnt_x = (float)X_tmp;
                        if (LBpnt_y > (float)Y_tmp) LBpnt_y = (float)Y_tmp;
                    }
                    float dist = (float)(5.0 / 0.0), real_x = 0, real_y = 0;
                    for (int bo_i = 0; bo_i < boundary_tmp.Count; bo_i++)//找与最近的实点
                    {
                        double X_tmp = boundary_tmp[bo_i].X;
                        double Y_tmp = boundary_tmp[bo_i].Y;
                        float the_dist = (float)Math.Sqrt(Math.Pow((X_tmp - LBpnt_x), 2) + Math.Pow((Y_tmp - LBpnt_y), 2));
                        if (the_dist < dist)
                        {
                            dist = the_dist;
                            real_x = (float)X_tmp;
                            real_y = (float)Y_tmp;
                        }
                    }
                    Point originPoint_new = new Point();
                    originPoint_new.X = real_x;
                    originPoint_new.Y = real_y;
                    tmpRoom.OriginPoint = originPoint_new;
                    room = tmpRoom;
                }
            });

            ////////////////////////////////////////////////////FMS接口
            if (boundary_mustbeChanged)
            {
                tempJson.RoomSettings = arr_roomlist_ori0;
            }
            else
            {
                tempJson.RoomList = roomSettingList;
                tempJson.RoomSettings = roomSettingList;
            }
            return boundary_mustbeChanged;
        }

        //lines：待融合线条
        //dist_threshold：线条相邻距离阈值，阈值之内表示两个线条处于同一位置
        //minlen：线条允许的最小长度，小于该值会被直接舍弃
        //offset：两个线段间隔如果小于offset则会进行合并
        private List<FusePf> Line_fuse(List<FusePf> bd_line, int dist_threshold, int minlen, int offset)
        {
            List<FusePf> res = new List<FusePf>();
            List<FusePf> lines_x = new List<FusePf>();
            List<FusePf> lines_y = new List<FusePf>();
            for (int fuse_i = 0; fuse_i < bd_line.Count(); fuse_i++)
            {
                int x1 = bd_line[fuse_i].X1;
                int x2 = bd_line[fuse_i].X2;
                if (Math.Abs(x1 - x2) <= 1)
                {
                    lines_x.Add(bd_line[fuse_i]);
                }
                int y1 = bd_line[fuse_i].Y1;
                int y2 = bd_line[fuse_i].Y2;
                if (Math.Abs(y1 - y2) <= 1)
                {
                    lines_y.Add(bd_line[fuse_i]);
                }
            }
            //竖直线，左到右排列,升序
            lines_x.Sort((a, b) => a.X1.CompareTo(b.X1));
            //水平线，下到上排列，升序
            lines_y.Sort((a, b) => a.Y1.CompareTo(b.Y1));

            List<List<FusePf>> tmp = new List<List<FusePf>>();
            List<FusePf> group = new List<FusePf>();
            //竖直线，相邻线条合并到一个分组
            int p_last = 0;
            for (int fuse_xi = 0; fuse_xi < lines_x.Count(); fuse_xi++)
            {
                int tmp_y1 = Math.Min(lines_x[fuse_xi].Y1, lines_x[fuse_xi].Y2);
                int tmp_y2 = Math.Max(lines_x[fuse_xi].Y1, lines_x[fuse_xi].Y2);
                lines_x[fuse_xi].Y1 = tmp_y1;
                lines_x[fuse_xi].Y2 = tmp_y2;
                if (lines_x[fuse_xi].X1 - p_last > dist_threshold)
                {
                    if (group.Count() > 0)
                    {
                        List<FusePf> tmp_group = new List<FusePf>(group);
                        tmp.Add(tmp_group);
                    }
                    group.Clear();
                    group.Add(lines_x[fuse_xi]);
                }
                else
                {
                    group.Add(lines_x[fuse_xi]);
                }
                p_last = lines_x[fuse_xi].X1;
            }
            if (group.Count() > 0)
            {
                List<FusePf> tmp_group = new List<FusePf>(group);
                tmp.Add(tmp_group);
            }
            //组内线条合并
            for (int tmp_i = 0; tmp_i < tmp.Count(); tmp_i++)
            {
                List<FusePf> r = new List<FusePf>();
                r = Lines_group_merge(tmp[tmp_i], minlen, offset, 0);
                res.AddRange(r);
            }

            tmp.Clear();
            group.Clear();
            //水平线，相邻线条合并到一个分组
            p_last = 0;
            for (int fuse_yi = 0; fuse_yi < lines_y.Count(); fuse_yi++)
            {
                int tmp_x1 = Math.Min(lines_y[fuse_yi].X1, lines_y[fuse_yi].X2); ;
                int tmp_x2 = Math.Max(lines_y[fuse_yi].X1, lines_y[fuse_yi].X2);
                lines_y[fuse_yi].X1 = tmp_x1;
                lines_y[fuse_yi].X2 = tmp_x2;
                if (lines_y[fuse_yi].Y1 - p_last > dist_threshold)
                {
                    if (group.Count() > 0)
                    {
                        List<FusePf> tmp_group = new List<FusePf>(group);
                        tmp.Add(tmp_group);
                    }
                    group.Clear();
                    group.Add(lines_y[fuse_yi]);
                }
                else
                {
                    group.Add(lines_y[fuse_yi]);
                }
                p_last = lines_y[fuse_yi].Y1;
            }
            if (group.Count() > 0)
            {
                List<FusePf> tmp_group = new List<FusePf>(group);
                tmp.Add(tmp_group);
            }
            //组内线条合并
            for (int tmp_i = 0; tmp_i < tmp.Count(); tmp_i++)
            {
                List<FusePf> r = new List<FusePf>();
                r = Lines_group_merge(tmp[tmp_i], minlen, offset, 1);
                res.AddRange(r);
            }
            return res;
        }

        //lines：待融合线条
        //minlen：线条允许的最小长度，小于该值会被直接舍弃
        //offset：两个线段间隔如果小于offset则会进行合并
        //flag：0表示合并竖线，1表示合并横线
        private List<FusePf> Lines_group_merge(List<FusePf> lines, int minlen, int offset, int flag)
        {
            if (lines.Count() == 0)
            {
                return lines;//为空返回
            }
            List<FusePf> res = new List<FusePf>();
            //竖直线处理
            if (flag == 0)
            {
                //将竖直线条在竖直方向排序，下到上，升序
                lines.Sort((a, b) => a.Y1.CompareTo(b.Y1));

                //以最长线的位置、方向为最终线条的位置和方向
                FusePf pl = new FusePf();
                int max_len = -1;
                for (int i = 0; i < lines.Count(); i++)
                {
                    if (lines[i].Y2 - lines[i].Y1 > max_len)
                    {
                        max_len = lines[i].Y2 - lines[i].Y1;
                        pl = lines[i];
                    }
                }
                int xp = pl.X1;
                int dep = pl.Depth, typ = pl.Type, dip = pl.Direction;
                int y10 = 0, y20 = 0;
                for (int i = 0; i < lines.Count(); i++)
                {
                    int y1 = lines[i].Y1, y2 = lines[i].Y2;
                    if (i == 0)
                    {
                        y10 = lines[i].Y1;
                        y20 = lines[i].Y2;
                        continue;
                    }
                    int pmin = Math.Max(y10, y1);
                    int pmax = Math.Min(y20, y2);
                    if (pmax - pmin + offset > 0)
                    {//线条有重叠，或者距离在offset之内，则进行叠加融合
                        y10 = Math.Min(y10, y1);
                        y20 = Math.Max(y20, y2);
                    }
                    else
                    {
                        //舍弃长度小于minlen的线条
                        if (y20 - y10 > minlen)
                        {
                            FusePf tmp = new FusePf();
                            tmp.Depth = dep;
                            tmp.Direction = dip;
                            tmp.Type = typ;
                            tmp.X1 = xp;
                            tmp.Y1 = y10;
                            tmp.X2 = xp;
                            tmp.Y2 = y20;
                            res.Add(tmp);
                        }
                        y10 = lines[i].Y1;
                        y20 = lines[i].Y2;
                    }
                }
                //舍弃长度小于minlen的线条
                if (y20 - y10 > minlen)
                {
                    FusePf tmp = new FusePf();
                    tmp.Depth = dep;
                    tmp.Direction = dip;
                    tmp.Type = typ;
                    tmp.X1 = xp;
                    tmp.Y1 = y10;
                    tmp.X2 = xp;
                    tmp.Y2 = y20;
                    res.Add(tmp);
                }
            }
            else
            {//水平线条
             //以下为横向线条处理，处理逻辑与上述竖直线条处理一致
             //水平线条在水平方向排序，左到右，升序
                lines.Sort((a, b) => a.X1.CompareTo(b.X1));

                //以最长线的位置、方向为最终线条的位置和方向
                FusePf pl = new FusePf();
                int max_len = -1;
                for (int i = 0; i < lines.Count(); i++)
                {
                    if (lines[i].X2 - lines[i].X1 > max_len)
                    {
                        max_len = lines[i].X2 - lines[i].X1;
                        pl = lines[i];
                    }
                }
                int yp = pl.Y1;
                int dep = pl.Depth, typ = pl.Type, dip = pl.Direction;
                int x10 = 0, x20 = 0;
                for (int i = 0; i < lines.Count(); i++)
                {
                    int x1 = lines[i].X1, x2 = lines[i].X2;
                    if (i == 0)
                    {
                        x10 = lines[i].X1;
                        x20 = lines[i].X2;
                        continue;
                    }
                    int pmin = Math.Max(x10, x1);
                    int pmax = Math.Min(x20, x2);
                    if (pmax - pmin + offset > 0)
                    {//线条有重叠，或者距离在offset之内，则进行叠加融合
                        x10 = Math.Min(x10, x1);
                        x20 = Math.Max(x20, x2);
                    }
                    else
                    {
                        //舍弃长度小于minlen的线条
                        if (x20 - x10 > minlen)
                        {
                            FusePf tmp = new FusePf();
                            tmp.Depth = dep;
                            tmp.Direction = dip;
                            tmp.Type = typ;
                            tmp.X1 = x10;
                            tmp.Y1 = yp;
                            tmp.X2 = x20;
                            tmp.Y2 = yp;
                            res.Add(tmp);
                        }
                        x10 = lines[i].X1;
                        x20 = lines[i].X2;
                    }
                }
                //舍弃长度小于minlen的线条
                if (x20 - x10 > minlen)
                {
                    FusePf tmp = new FusePf();
                    tmp.Depth = dep;
                    tmp.Direction = dip;
                    tmp.Type = typ;
                    tmp.X1 = x10;
                    tmp.Y1 = yp;
                    tmp.X2 = x20;
                    tmp.Y2 = yp;
                    res.Add(tmp);
                }
            }
            return res;
        }
    }
}